/*
 * Copyright 2014 Graham Smith
 */
package uk.co.wobblycogs.vegas.riff;

import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.util.ArrayList;
import java.util.List;

/**
 *
 * @author Graham Smith
 */
public class AbstractRIFFObject implements RIFFObject {

	private byte[] rawType;
	private RIFFType type;
	private int length;
	protected byte[] signature;
	private byte[] data;
	private final List<RIFFObject> children = new ArrayList<>();

	public AbstractRIFFObject() {
	}

	@Override
	public byte[] getRawType() {
		return rawType;
	}

	protected void setRawType(byte[] rawType) {
		this.rawType = rawType;
	}

	@Override
	public RIFFType getType() {
		return type;
	}

	protected void setType(RIFFType type) {
		this.type = type;
	}

	@Override
	public int getLength() {
		return length;
	}

	protected void setLength(int length) {
		this.length = length;
	}

	@Override
	public byte[] getSignature() {
		return signature;
	}

	protected void setSignature(byte[] signature) {
		this.signature = signature;
	}

	public String getStringSignature() {
		return RIFFUtilities.bytesToHex(getSignature());
	}

	@Override
	public byte[] getData() {
		return data;
	}

	protected void setData(byte[] data) {
		this.data = data;
	}

	@Override
	public void load(ByteBuffer byteBuffer) {
		//Ensure that the buffer is set to little endian
		ByteBuffer bb = byteBuffer.order(ByteOrder.LITTLE_ENDIAN);

		//The first sixteen bytes tell us the type of the chunk being delt
		//with. There are two obvious type RIFF and LIST and everything else
		//is some type of data holding CHUNK.
		rawType = new byte[16];
		bb.get(rawType);
		type = RIFFType.getRIFFType(rawType);

		//The next four (perhaps eight) bytes give us the length of the chunk.
		//This is the complete length including the type and length bytes.
		length = bb.getInt();
		RIFFUtilities.move(bb, 4);

		if (type == RIFFType.RIFF || type == RIFFType.LIST) {
			//If the type is RIFF or LIST then there is a sixteen byte
			//signature that can be extracted.
			signature = new byte[16];
			bb.get(signature);

			//After the signature there is a list of additional chunks. Processing
			//is complete once there are no more bytes to read.
			while (bb.remaining() > 0) {
				//Find out what type the child is.
				byte[] childTypeArray = new byte[16];
				bb.get(childTypeArray);
				RIFFType childType = RIFFType.getRIFFType(childTypeArray);

				//Determine the childs size
				int childSize = bb.getInt();

				//Rewind a bit and then get the child data.
				RIFFUtilities.move(bb, -20);
				byte[] childData = new byte[childSize];
				bb.get(childData);

				//Instantiate the appropriate child type
				RIFFObject child;
				switch (childType) {
					case RIFF:
						throw new RuntimeException("There should only be a single RIFF section in a file.");
					case LIST:
						child = new RIFFList();
						break;
					case HEADER:
						child = new RIFFHeader();
						break;
					case RESOURCE:
						child = new RIFFResource();
						break;
					default:
						child = new RIFFChunk();
				}

				//Load the child and
				ByteBuffer childBB = ByteBuffer.wrap(childData);
				childBB = childBB.order(ByteOrder.LITTLE_ENDIAN);
				child.load(childBB);
				addChild(child);
			}
		} else {
			//This is a chunk so has no children. Place the data into the array
			//and terminate the recursion by using up the buffer.
			signature = new byte[0];
			RIFFUtilities.move(bb, -24);
			data = new byte[length];
			bb.get(data);
		}

	}

	@Override
	public List<RIFFObject> getChildren() {
		return children;
	}

	protected void addChild(RIFFObject child) {
		children.add(child);
	}
}
